/* 
 * File:   Partida.cpp
 * Author: fibrizo
 * 
 * Created on 24 de mayo de 2014, 17:45
 */

#include "Partida.h"
#include "ParserXML.h"
#include "Nivel.h"
#include "Configuracion.h"
#include <vector>
#include <string>

using std::vector;
using std::string;
using std::stoul;
using std::stof;

typedef struct vector<Nivel> vectorN;
typedef struct map<string, Jugador> hash;

/********************************************************************/
/*                           CONSTRUCTORES                          */
/********************************************************************/

Partida::Partida(Jugador& host) {
    this->cantidadJugadoresActual = 1;
    this->jugadores[host.getNombre()] = host;
    this->lista = false;
    this->campaniaCargada = false;
    this->Jugando = false;
}

/********************************************************************/
/********************************************************************/

Partida::Partida(Jugador& host, string rutaArchivoCampania) {
    this->cantidadJugadoresActual = 1;
    this->jugadores[host.getNombre()] = host;
    this->lista = false;
    this->campaniaCargada = false;
    this->Jugando = false;
    this->cargarCampania(rutaArchivoCampania);
}

/********************************************************************/
/********************************************************************/

Partida::Partida(const Partida& orig) {
}

/********************************************************************/
/*                          METODOS PRIVADOS                        */
/********************************************************************/

// Recibe por parametro un vector con los datos de N niveles.
// Esta funcion extrae de dicho vector los datos referentes
// a zombies, crea N niveles y les agrega a los zombies que
// correspondan a cada uno.
// Devuelve una vector de datos del tipo Nivel.
vectorN Partida::crearNiveles(vector<string> datos) {
    vectorN niveles;
    unsigned int i = 4;
    size_t verdes = 0;
    size_t azules = 0;
    size_t rojos = 0;
    
    unsigned int indiceCosasNivel = 0;
    while(i < datos.size()) {
        verdes = stoul(datos[i+2], NULL, 10);
        azules = stoul(datos[i+3], NULL, 10);
        rojos = stoul(datos[i+4], NULL, 10);
        Nivel nivel = Nivel(Configuracion::getTamanioMapa());
        nivel.setZombies(verdes, azules, rojos);
        niveles.push_back(nivel);
        indiceCosasNivel++;
        if (indiceCosasNivel != 6) {
            indiceCosasNivel = 0;
            i += 4;
        }
        i++;
    }
    return niveles;
}


/********************************************************************/
/*                          METODOS PUBLICOS                        */
/********************************************************************/

bool Partida::cargarCampania(string rutaArchivoCampania) {
    ParserXML parser;
    vector<string> datos = parser.cargarDatos(rutaArchivoCampania);
    vector<Nivel> niveles = crearNiveles(datos);
    this->campania = Campania(datos[0], stoul(datos[2]), niveles, datos[1], stoul(datos[3]));
    this->campaniaCargada = true;
    return true;
}

/********************************************************************/
/********************************************************************/

bool Partida::cargarDatosDeConfiguracion(string rutaArchivoConfiguracion) {
    ParserXML parser;
    vector<string> datos = parser.cargarDatos(rutaArchivoConfiguracion);
    // Bombas
    this->alcanceBombas = stoul(datos[0]);
    this->tiempoDetonacion = stof(datos[1]);
    // Otros tiempos
    this->tiempRefresco = stof(datos[2]);
    this->tiempoRegerenacionBomba = stof(datos[3]);
    // Velocidades de desplazamiento
    this->tiempoZombieVerde = stof(datos[4]);
    this->tiempoBomberman = stof(datos[5]);
    this->tiempoDisparo = stof(datos[6]);
    // Mapa
    this->tamanioMapa = stoul(datos[7]);
    return true;
}

/********************************************************************/
/********************************************************************/

bool Partida::estaListaParaComenzar() {
    if (!this->campaniaCargada) return false;
    if (this->cantidadJugadoresActual < this->cantidadJugadoresRequerida)
        return false;
    return true;
}

/********************************************************************/
/********************************************************************/

void Partida::agregarJugador(Jugador& nuevoJugador) {
    this->jugadores[nuevoJugador.getNombre()] = nuevoJugador;
    this->cantidadJugadoresActual++;
    this->lista = this->estaListaParaComenzar();
}

/********************************************************************/
/********************************************************************/

void Partida::eliminarJugador(Jugador jugador) {
    this->jugadores.erase(jugador.getNombre());
    this->cantidadJugadoresActual--;
    this->lista = this->estaListaParaComenzar();
}

/********************************************************************/
/********************************************************************/

bool Partida::jugadoresListos() {
    for(hash::iterator it = this->jugadores.begin(); it != this->jugadores.end(); it++) {
        Jugador actual = it->second;
        if (!actual.estaListo()) return false;
    }
    return true;
}

/********************************************************************/
/********************************************************************/

void Partida::actualizarEstado() {
    for(unsigned int i = 0; i < this->colaDeEventos.size(); i++) {
        Evento nuevoEvento = colaDeEventos.front();
        colaDeEventos.pop();
        Jugador jugadorDisparoEvento = nuevoEvento.obtenerJugador();
        Bomberman personaje = jugadorDisparoEvento.obtenerPersonaje();
        if (nuevoEvento.esDireccion())
            personaje.setDireccion(nuevoEvento.getNombre());
        else
            personaje.crearBomba();
    }
    this->nivelActual.actualizar();
}

bool Partida::nivelActualTerminado() {
    return (this->nivelActual).termino();
}

// Si la cantidad de jugadores es la requerida, comienza la partida
// en caso contrario se sale de la funcion.
// Devuelve True si la cantidad de jugadores era la requerida para
// comenzar la partida y esta comenzo. Devuelve False en caso 
// contrario.
bool Partida::correr() {
    while (!this->terminada) {
        if ((!this->lista) || (!this->jugadoresListos()))
            return false;
        this->Jugando = true;
        this->campania.comenzarSiguienteNivel();
        this->nivelActual = this->campania.getNivelActual();
        while (!this->nivelActualTerminado()) {
            this->actualizarEstado();
        }
    }
    this->Jugando = false;
    return true;
}

/********************************************************************/
/********************************************************************/

Campania Partida::obtenerCampania() {
    return this->campania;
}

/********************************************************************/
/*                              DESTRUCTOR                          */
/********************************************************************/

Partida::~Partida() {
}

